#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <errno.h>
#include <ctype.h>
#include <sys/epoll.h>

#include "wrap.h"

#define MAXLINE 8192
#define SERV_PORT 7777
#define OPEN_MAX 5000

int main(){
    int listenfd = Socket(AF_INET,SOCK_STREAM,0);
    int opt = 1;
    setsockopt(listenfd,SOL_SOCKET,SO_REUSEPORT,(void*)&opt,sizeof(opt));//端口复用

    struct sockaddr_in servaddr;
    bzero(&servaddr,sizeof(servaddr));
    servaddr.sin_family = AF_INET;//IPv4
    servaddr.sin_port = htons(SERV_PORT);//端口
    servaddr.sin_addr.s_addr = INADDR_ANY;//本主机的任意IP都可以使用

    Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
    Listen(listenfd,20);
    printf("listen success!\n");

    int efd = epoll_create(OPEN_MAX); //创建epoll模型，efd指向红黑树的根节点
    if(efd == -1){
        perr_exit("epoll_create error");
    }

    struct epoll_event tep;
    tep.events = EPOLLIN; tep.data.fd = listenfd;//指定lfd的监听事件为 读

    int res = epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep); //将lfd及对应的结构体设置到树上，efd可以找到树
    if (res == -1){
        perr_exit("epoll_ctl error");
    }

    struct sockaddr_in cliaddr;
    int clilen = sizeof(cliaddr);
    int nready = 0;
    int connfd;
    int num = 0;
    struct epoll_event ep[OPEN_MAX];
    char buf[MAXLINE],str[INET_ADDRSTRLEN];
    for(;;){
        //epoll为sever阻塞监听事件，ep为struct epoll_event类型数组，OPEN_MAX为数组容量，-1表示永久阻塞
        nready = epoll_wait(efd,ep,OPEN_MAX,-1);
        if (nready == -1){
            perr_exit("epoll_wait error");
        }

        for (int i =0 ; i < nready;i++){
            if(!(ep[i].events & EPOLLIN)){//如果不是读事件，继续循环
                continue;
            }
            if(ep[i].data.fd == listenfd){//判断满足事件的fd而不是lfd
                connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&clilen);
                printf("reveive from %s at port %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr.s_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
                printf("cfd %d -- client %d\n",connfd,++num);
                tep.events = EPOLLIN; tep.data.fd=connfd;
                int res = epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep); //客户端的fd加入监听
                if (res == -1){
                    perr_exit("epoll_ctl error");
                }

            }else{ //不是lfd
                int sockfd = ep[i].data.fd;
                int n = Read(ep[i].data.fd,buf,MAXLINE);
                if(n == 0){
                    int res = epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL); //读到0说明客户端已经关闭，从红黑树中移除
                    if (res == -1){
                        perr_exit("epoll_ctl err");
                    }

                    Close(sockfd);

                    printf("client[%d] close connection\n",sockfd);
                }else if(n <0){//出错
                    perror("read n < 0 error");
                    int res = epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);
                    Close(sockfd);
                }else{
                    for(int i = 0; i < n; i++){
                        buf[i] = toupper(buf[i]);
                    }
                    Write(STDOUT_FILENO,buf,n);
                    Writen(sockfd,buf,n);
                }

            }

        }

    }

    Close(listenfd);


}